/***
 * 
 * @author zhoujunhui
 */
package net.wicp.tams.common.spring.beans;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.codahale.metrics.Counter;

import net.wicp.tams.common.Conf;
import net.wicp.tams.common.apiext.ReflectAssist;
import net.wicp.tams.common.apiext.StringUtil;
import net.wicp.tams.common.connector.executor.impl.CommonService;
import net.wicp.tams.common.constant.StrPattern;
import net.wicp.tams.common.exception.ExceptAll;
import net.wicp.tams.common.exception.ProjectExceptionRuntime;
import net.wicp.tams.common.spring.SpringAssit;
import net.wicp.tams.common.spring.annotation.binlog.BinlogListener;
import net.wicp.tams.common.spring.annotation.connector.CacheConnector;
import net.wicp.tams.common.spring.annotation.metrics.CounterTams;
import net.wicp.tams.common.spring.annotation.metrics.GaugeTams;
import net.wicp.tams.common.spring.annotation.metrics.HistogramTams;
import net.wicp.tams.common.spring.annotation.metrics.MeterTams;
import net.wicp.tams.common.spring.annotation.metrics.TimerTams;

public class AnnotationBean implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware {

	private String annotationPackage;

	private ApplicationContext applicationContext;

	public String getAnnotationPackage() {
		return annotationPackage;
	}

	public void setAnnotationPackage(String annotationPackage) {
		this.annotationPackage = annotationPackage;
	}

	public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
			throws BeansException {
		if (annotationPackage == null || annotationPackage.length() == 0) {
			return;
		}

		if (configurableListableBeanFactory instanceof BeanDefinitionRegistry) {
			try {
				// init scanner
				Class<?> scannerClass = ReflectAssist
						.forName("org.springframework.context.annotation.ClassPathBeanDefinitionScanner");
				Object scanner = scannerClass
						.getConstructor(new Class<?>[] { BeanDefinitionRegistry.class, boolean.class })
						.newInstance(new Object[] { (BeanDefinitionRegistry) configurableListableBeanFactory, true });
				// add filter
				Class<?> filterClass = ReflectAssist
						.forName("org.springframework.core.type.filter.AnnotationTypeFilter");
				Object filter = filterClass
						.getConstructor(Class.class, Class.class, Class.class, Class.class, Class.class).newInstance(
								Counter.class, GaugeTams.class, HistogramTams.class, MeterTams.class, TimerTams.class);
				Method addIncludeFilter = scannerClass.getMethod("addIncludeFilter",
						ReflectAssist.forName("org.springframework.core.type.filter.TypeFilter"));
				addIncludeFilter.invoke(scanner, filter);
				// scan packages
				String[] packages = StrPattern.split_pattern.compile().split(annotationPackage);
				Method scan = scannerClass.getMethod("scan", new Class<?>[] { String[].class });
				scan.invoke(scanner, new Object[] { packages });
			} catch (Throwable e) {

			}
		}

	}

	@Override
	public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
		return o;
	}

	// public Map<String, Counter> countermap=new HashMap<>();
	@Override
	public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
		/*
		 * if( o.getClass().getName().contains("TestController")) {
		 * if(AnnotationUtils.findAnnotation(o.getClass(), Controller.class) != null) {
		 * System.out.println(4); } System.out.println(3); List<Field> selectFields =
		 * selectFields(o.getClass()); System.out.println("selectFields="+selectFields);
		 * }
		 */

		CacheConnector cacheConnector = o.getClass().getDeclaredAnnotation(CacheConnector.class);
		if (cacheConnector != null) {
			CommonService commonService = this.applicationContext.getBean(CommonService.class);
			String[] beanNamesForType = this.applicationContext.getBeanNamesForType(o.getClass());
			for (String beanName : beanNamesForType) {
				commonService.needCacheMap.put(beanName, cacheConnector.expire());
			}
		}
		// binlog监听支持
		BinlogListener binlogListener = o.getClass().getDeclaredAnnotation(BinlogListener.class);
		if (binlogListener != null) {
			Properties props = new Properties();
			String preStr = String.format("common.binlog.alone.conf.%s.", binlogListener.conf());
			if (StringUtil.isNotNull(binlogListener.groupId())) {
				props.put(preStr + "groupId", binlogListener.groupId());
			}
			if (StringUtil.isNotNull(binlogListener.haType())) {
				props.put(preStr + "haType", binlogListener.haType());
			}
			props.put(preStr + "chk", binlogListener.chk());
			if (StringUtil.isNotNull(binlogListener.ip())) {
				props.put(preStr + "ip", binlogListener.ip());
			}
			props.put(preStr + "port", String.valueOf(binlogListener.port()));
			if (StringUtil.isNotNull(binlogListener.user())) {
				props.put(preStr + "user", binlogListener.user());
			}
			if (StringUtil.isNotNull(binlogListener.password())) {
				props.put(preStr + "password", binlogListener.password());
			}
			if (StringUtil.isNotNull(binlogListener.dbPattern())) {
				props.put(preStr + "dbPattern", binlogListener.dbPattern());
			}
			if (StringUtil.isNotNull(binlogListener.tbPattern())) {
				props.put(preStr + "tbPattern", binlogListener.tbPattern());
			}
			props.put(preStr + "rds", String.valueOf(binlogListener.rds()));
			props.put(preStr + "clientId", String.valueOf(binlogListener.clientId()));
			if (StringUtil.isNotNull(binlogListener.pos_gtids())) {
				props.put(preStr + "pos.gtids", binlogListener.pos_gtids());
			}
			props.put(preStr + "listener", o.getClass().getName());// 添加监听类
			Conf.overProp(props);
			try {
				ReflectAssist.invokeStaticMothed("net.wicp.tams.common.spring.binlog.SpringStartBinlogListener",
						"startListener", new Class[] { String.class }, binlogListener.conf());
			} catch (Exception e) {// 可能没有引入common-binlog-alone
				throw new ProjectExceptionRuntime(ExceptAll.project_nosupport, e);
			}
		}

		doWithFields(o, s, CounterTams.class);
		doWithFields(o, s, HistogramTams.class);
		doWithFields(o, s, TimerTams.class);
		doWithFields(o, s, GaugeTams.class);
		doWithFields(o, s, MeterTams.class);
		return o;
	}

	private void doWithFields(Object o, String s, Class<? extends Annotation> classA) {
		List<Field> findFields = SpringAssit.selectFields(o.getClass(), classA);
		if (CollectionUtils.isNotEmpty(findFields)) {// 含有Conter
			for (Field field : findFields) {
				Annotation declaredAnnotation = field.getDeclaredAnnotation(classA);
				MetricsBean<Object> serviceBean = new MetricsBean<Object>(declaredAnnotation, field);
				serviceBean.setApplicationContext(applicationContext);
				serviceBean.setRef(o);
				try {
					serviceBean.afterPropertiesSet();
				} catch (Exception e) {
					throw new ProjectExceptionRuntime(ExceptAll.Project_default, "设置属性【" + s + "】失败");
				}
			}
		}
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

}
